// ============================================================================
// cdc_pack.v  (Verilog-2001)
// 常用 CDC 组件打包：
//  1) cdc_bit_sync        : 单比特两级同步器（打拍去亚稳）
//  2) cdc_bus_handshake   : 多位总线“请求/确认”握手（lossless, 有背压）
//  3) cdc_apply_on_edge   : 目的域边界(如 LRCLK 上升沿)原子切换为 active
//  4) sample_mailbox_cdc  : 单样本邮箱式 CDC（lossless, 适合音频每采样）
//
// 用法摘要：
//  A) time_delay_calc (clk域) -> cdc_bus_handshake -> (i2s_bclk域 shadow)
//     -> cdc_apply_on_edge 对齐 LRCLK 上升沿 -> delays_active 给 sig_add
//  B) sig_add (i2s_bclk域) 的 out_proc_data/_vld -> sample_mailbox_cdc
//     -> (clk域) i2s_transmit_top
//
// 注意：请在约束中把 clk 与 i2s_bclk 分到异步组；并对同步器首拍适当 set_false_path。
// ============================================================================


// --------------------------------------------------------------------------
// 1) cdc_bit_sync : 单比特两级同步器
//   - 适用于将异步/跨域单bit电平同步到目标时钟域
// --------------------------------------------------------------------------
module cdc_bit_sync
(
  input  wire clk,       // 目标域时钟
  input  wire rst_n,     // 异步低有效复位
  input  wire din_async, // 异步/跨域输入
  output reg  dout_sync  // 同步后的稳定电平
);
  reg d1;
  always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
      d1        <= 1'b0;
      dout_sync <= 1'b0;
    end else begin
      d1        <= din_async;
      dout_sync <= d1;
    end
  end
endmodule


// --------------------------------------------------------------------------
// 2) cdc_bus_handshake : 多位总线“请求/确认”握手 (src->dst)
//   - src 侧：src_req_pulse 且不 busy -> 锁存 src_bus 并翻转 req_toggle
//   - dst 侧：检测到 req_toggle 边沿 -> 两拍向量同步后锁存到 dst_bus_latched，
//             输出 1 拍 dst_update_pulse，回传 ack_toggle（lossless/有背压）
//   - src_busy = (req_toggle ^ ack_toggle_sync)，忙时不接受新请求
//   - 要求：src_bus 在 busy 期间保持稳定（本模块已在 src 侧内部保持）
// --------------------------------------------------------------------------
module cdc_bus_handshake #(
  parameter integer WIDTH = 256
)(
  // 源域
  input  wire              src_clk,
  input  wire              src_rst_n,
  input  wire [WIDTH-1:0]  src_bus,
  input  wire              src_req_pulse,  // 单拍/电平均可；busy=0 时生效
  output wire              src_busy,       // 1=正在等待目的域确认

  // 目的域
  input  wire              dst_clk,
  input  wire              dst_rst_n,
  output reg  [WIDTH-1:0]  dst_bus_latched, // 两拍向量同步后锁存的总线
  output reg               dst_update_pulse // 捕获到新配置的1拍脉冲
);

  // 源域：保持需要传输的总线 & 请求翻转
  reg [WIDTH-1:0] src_bus_hold;
  reg req_tgl;

  // 目的域->源域：ack toggle 返回，并在源域两拍同步
  reg ack_tgl;             // 目的域产生
  reg ack_sync1, ack_sync2;// 源域同步
  wire ack_tgl_sync = ack_sync2;

  assign src_busy = (req_tgl ^ ack_tgl_sync);

  // 源域逻辑
  always @(posedge src_clk or negedge src_rst_n) begin
    if(!src_rst_n) begin
      src_bus_hold <= {WIDTH{1'b0}};
      req_tgl      <= 1'b0;
      ack_sync1    <= 1'b0;
      ack_sync2    <= 1'b0;
    end else begin
      // 同步目的域 ack toggle
      ack_sync1 <= ack_tgl;
      ack_sync2 <= ack_sync1;

      // 新请求，且不忙 -> 锁存总线并翻转 req_tgl
      if (src_req_pulse && !src_busy) begin
        src_bus_hold <= src_bus;
        req_tgl      <= ~req_tgl;
      end
    end
  end

  // 目的域：同步 req toggle + 两拍向量同步总线
  reg req_d1, req_d2;
  wire req_edge = (req_d1 ^ req_d2);

  reg [WIDTH-1:0] bus_sync_d1, bus_sync_d2;

  always @(posedge dst_clk or negedge dst_rst_n) begin
    if(!dst_rst_n) begin
      req_d1           <= 1'b0;
      req_d2           <= 1'b0;
      bus_sync_d1      <= {WIDTH{1'b0}};
      bus_sync_d2      <= {WIDTH{1'b0}};
      dst_bus_latched  <= {WIDTH{1'b0}};
      dst_update_pulse <= 1'b0;
      ack_tgl          <= 1'b0;
    end else begin
      // 同步 req toggle
      req_d1 <= req_tgl;
      req_d2 <= req_d1;

      // 两拍向量同步（总线在握手期间由 src_bus_hold 保持稳定）
      bus_sync_d1 <= src_bus_hold;
      bus_sync_d2 <= bus_sync_d1;

      // 边沿到达：锁存 + 发出更新脉冲 + 回 ack
      dst_update_pulse <= req_edge;
      if (req_edge) begin
        dst_bus_latched <= bus_sync_d2;
        ack_tgl         <= req_d2;
      end
    end
  end

endmodule


// --------------------------------------------------------------------------
// 3) cdc_apply_on_edge : 目的域“对齐边界再生效”的原子切换
//   - 将已在目的域锁存好的 shadow 总线（如 cdc_bus_handshake 输出）
//     延迟到指定的对齐边界（如 LRCLK 上升沿）再原子切换为 active
//   - update_pulse 通常接 cdc_bus_handshake 的 dst_update_pulse
//   - edge_pulse   为目的域的一拍对齐脉冲（如 LRCLK 上升沿检测）
// --------------------------------------------------------------------------
module cdc_apply_on_edge #(
  parameter integer WIDTH = 256
)(
  input  wire              clk,              // 目的域时钟（如 i2s_bclk）
  input  wire              rst_n,
  input  wire [WIDTH-1:0]  shadow_bus,       // 已在目的域锁存的“影子”总线
  input  wire              update_pulse,     // 新数据到达（1拍）
  input  wire              edge_pulse,       // 对齐边界（1拍，如 LRCLK 上升沿）
  output reg  [WIDTH-1:0]  active_bus        // 对齐边界处原子生效后的总线
);

  reg pending;

  always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
      pending    <= 1'b0;
      active_bus <= {WIDTH{1'b0}};
    end else begin
      if (update_pulse)
        pending <= 1'b1;
      if (edge_pulse && pending) begin
        active_bus <= shadow_bus; // 原子切换
        pending    <= 1'b0;
      end
    end
  end

endmodule


// --------------------------------------------------------------------------
// 4) sample_mailbox_cdc : 单样本邮箱式 CDC（源域->目的域，lossless）
//   - 适合音频一采样一个数据：
//       src_valid & src_ready -> 锁存 data 并翻转 req；dst 看到边沿后两拍同步
//       取走数据，打一拍 dst_valid，并翻转 ack
//   - 要求：源域写入速率 <= 目的域消费速率（不堆积）；否则建议用小深度异步 FIFO
// --------------------------------------------------------------------------
module sample_mailbox_cdc #(
  parameter integer WIDTH = 16
)(
  // 源域（写） e.g. i2s_bclk
  input  wire              src_clk,
  input  wire              src_rst_n,
  input  wire [WIDTH-1:0]  src_data,
  input  wire              src_valid,   // 单拍或电平均可
  output wire              src_ready,   // 1=邮箱空，可写

  // 目的域（读） e.g. 50MHz
  input  wire              dst_clk,
  input  wire              dst_rst_n,
  output reg  [WIDTH-1:0]  dst_data,
  output reg               dst_valid    // 1拍脉冲
);

  // 源域：邮箱 + 请求翻转
  reg [WIDTH-1:0] data_hold;
  reg req_tgl;

  // 目的域->源域：ack toggle 返回，并在源域两拍同步
  reg ack_tgl;
  reg ack_sync1, ack_sync2;
  wire ack_sync = ack_sync2;

  assign src_ready = (req_tgl == ack_sync); // 相等表示邮箱空

  always @(posedge src_clk or negedge src_rst_n) begin
    if(!src_rst_n) begin
      data_hold <= {WIDTH{1'b0}};
      req_tgl   <= 1'b0;
      ack_sync1 <= 1'b0;
      ack_sync2 <= 1'b0;
    end else begin
      // 同步 ack
      ack_sync1 <= ack_tgl;
      ack_sync2 <= ack_sync1;

      if (src_valid && src_ready) begin
        data_hold <= src_data;
        req_tgl   <= ~req_tgl;
      end
    end
  end

  // 目的域：同步 req + 两拍向量同步 + 回 ack
  reg req_d1, req_d2;
  wire req_edge = (req_d1 ^ req_d2);

  reg [WIDTH-1:0] d1, d2;

  always @(posedge dst_clk or negedge dst_rst_n) begin
    if(!dst_rst_n) begin
      req_d1   <= 1'b0; req_d2 <= 1'b0;
      d1       <= {WIDTH{1'b0}};
      d2       <= {WIDTH{1'b0}};
      dst_data <= {WIDTH{1'b0}};
      dst_valid<= 1'b0;
      ack_tgl  <= 1'b0;
    end else begin
      // 同步 req toggle
      req_d1 <= req_tgl;
      req_d2 <= req_d1;

      // 两拍向量同步
      d1 <= data_hold;
      d2 <= d1;

      // 采到新样本：打一拍 vld，回 ack
      dst_valid <= req_edge;
      if (req_edge) begin
        dst_data <= d2;
        ack_tgl  <= req_d2;
      end
    end
  end

endmodule

// ============================================================================
// End of cdc_pack.v
// ============================================================================
